{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# -简单线性回归到复杂神经网络\n",
    "# -人工求导到自动梯度下降  \n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 非线性数据 - sigmoid函数(\"S\"型)\n",
    "\n",
    "$$ f(x) = k2 * \\sigma(k_1 * x + b_1) + b2 $$\n",
    "\n",
    "$$ sigmoid(x) = \\sigma(x) = \\frac{1}{1 + e^{-x}} $$ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def sigmoid(x):\n",
    "    return 1 / (1 +np.exp(-x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7f9aa4f3f810>]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 测试sigmoid函数\n",
    "test_x = np.linspace(-10, 10)\n",
    "\n",
    "plt.plot(test_x, sigmoid(test_x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 测试多次随机sigmoid\n",
    "import random as rd\n",
    "def multiply_linear(x):\n",
    "    k, b = rd.random(), rd.random()\n",
    "    return k*x+b\n",
    "    \n",
    "for _ in range(5):\n",
    "    plt.plot(test_x, multiply_linear(sigmoid(test_x)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 神经网络\n",
    "- 借鉴人脑神经元的构造创建\n",
    "\n",
    "## 机器自动拟合函数\n",
    "- 我们可以通过基本的模块(线性+非线性)，经过反复的叠加，来实现更加复杂的函数\n",
    "- 理论上来说：一条直线 + 一条曲线 = 任意直线\n",
    "- 激活函数（激活神经元）：为了拟合非线性关系 - 用“曲线”打败“曲线”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support. ' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option);\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"640\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.animation.FuncAnimation at 0x7f9aa171dd10>"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def random_linear(x):\n",
    "    # k, b = rd.random(), rd.random() # 正数\n",
    "    k, b = rd.normalvariate(0, 1), rd.normalvariate(0, 1) # 有正有负\n",
    "    return k*x+b\n",
    "\n",
    "# 单层神经元\n",
    "def draw(index):\n",
    "        # 线性变换\n",
    "        i = rd.choice(range(len(test_x)))\n",
    "        linear_output = np.concatenate( (random_linear(test_x[:i]), random_linear(test_x[i:])) )\n",
    "        # 非线性变换\n",
    "        i2 = rd.choice(range(len(linear_output)))\n",
    "        output = np.concatenate( (sigmoid(linear_output[:i2]), sigmoid(linear_output[i2:])) )\n",
    "        fig.clear() # 每一次的情况\n",
    "        plt.plot(test_x, output, color='green')\n",
    "    \n",
    "# 动态画图\n",
    "from matplotlib.animation import FuncAnimation \n",
    "# %matplotlib notebook\n",
    "fig = plt.gcf()\n",
    "FuncAnimation(fig, draw, interval=900)"
   ]
  },
  {
   "attachments": {
    "%E5%9B%BE%E7%89%87.png": {
     "image/png": "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"
    }
   },
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 深度学习\n",
    "- 多层神经网络（>3, 局限：需要足够数据量支撑（层级+1，数据量级\\*10）） \n",
    "\n",
    "## 机器自动求导\n",
    "- 原理：链式求导\n",
    "![%E5%9B%BE%E7%89%87.png](attachment:%E5%9B%BE%E7%89%87.png)  \n",
    "\n",
    "- Q：给定函数关系f(k1,b1,k2,b2)，如何自动求偏导\n",
    "\n",
    "- A: 1-如何表示（存储）？图结构(graph)\n",
    "   2-如何求loss对k1的偏导？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support. ' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option);\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>');\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"640\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import networkx as nx\n",
    "\n",
    "node_x, node_k1, node_b1 = 'x', 'k1', 'b1'\n",
    "node_k2, node_b2 = 'k2', 'b2'\n",
    "node_linear_01, node_linear_02, node_sigmoid = 'linear_01', 'linear_02', 'sigmoid'\n",
    "node_loss = 'loss'\n",
    "\n",
    "computing_graph = { # represent model \n",
    "    node_x: [node_linear_01],\n",
    "    node_k1: [node_linear_01],\n",
    "    node_b1: [node_linear_01],\n",
    "    node_linear_01: [node_sigmoid],\n",
    "    node_sigmoid: [node_linear_02],\n",
    "    node_k2: [node_linear_02],\n",
    "    node_b2: [node_linear_02],\n",
    "    node_linear_02: [node_loss],\n",
    "}\n",
    "\n",
    "graph = nx.DiGraph(computing_graph)\n",
    "# layout = nx.layout.spring_layout(graph)\n",
    "nx.draw(graph, with_labels=True, node_color='red')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 实现对指定点自动求偏导\n",
    "仅梳理思路并print正确,实现求偏导见Mini_Frame"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 139,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 单个：找到并记录 指定点的 下一个结点(输出)\n",
    "def get_output(graph, n):\n",
    "    outputs = []\n",
    "    \n",
    "    for node, links in graph.items():\n",
    "        if node == n:\n",
    "            outputs += links # 找到单个输出就OK\n",
    "    return outputs\n",
    "\n",
    "# 多个：指定点 自动生成链式偏导 直到终点loss\n",
    "def get_paramter_partial_order(p):\n",
    "    computing_order = []\n",
    "    \n",
    "    target = p\n",
    "    out = get_output(computing_graph, target)[0] # 得到结点而不是列表\n",
    "    \n",
    "    # 添加首结点（指定点）\n",
    "    computing_order.append(target) \n",
    "    # 依次添加输出结点\n",
    "    while out:\n",
    "        computing_order.append(out) \n",
    "        # 得到含下个输出的列表\n",
    "        out = get_output(computing_graph, out) # 注意：防止末位索引溢出 \n",
    "        if out:\n",
    "            out = out[0]\n",
    "    \n",
    "    # 以偏导格式输出一下(需要reverse)\n",
    "    order = []\n",
    "    computing_order.reverse()\n",
    "    # print(computing_order)\n",
    "    for index, n in enumerate(computing_order):\n",
    "        if n == p: break # 解决溢出问题\n",
    "        order.append((n, computing_order[index+1]))\n",
    "\n",
    "    # print(order)\n",
    "    order.reverse()\n",
    "    return '*'.join(['∂{}/∂{}'.format(a, b) for a, b in order[::-1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\\partial$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 140,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "∂loss/∂linear_02*∂linear_02/∂sigmoid*∂sigmoid/∂linear_01*∂linear_01/∂k1\n",
      "∂loss/∂linear_02*∂linear_02/∂sigmoid*∂sigmoid/∂linear_01*∂linear_01/∂b1\n",
      "∂loss/∂linear_02*∂linear_02/∂k2\n",
      "∂loss/∂linear_02*∂linear_02/∂b2\n"
     ]
    }
   ],
   "source": [
    "for p in ['k1', 'b1','k2', 'b2']:\n",
    "    print(get_paramter_partial_order(p))\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 实现自动将重复的偏导存入内存并调用：拓扑排序"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> step1、任意选择一个没有输入的结点  \n",
    "> step2、在图中删去step1中选择的结点，将该结点作为路径顺序首结点  \n",
    "> step3、检查：为空结束，不为空返回step1  "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
